package com.atguigu.boot.controller;

import com.atguigu.boot.entities.Customer;
import com.atguigu.boot.service.CustomerService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName CustomerController
 * @Description 此类描述:对MySQL数据库进行增删改查操作,同时将MySQL数据库操作同步到redis数据库中(扩展解决缓存击穿问题)
 * @Author dangchen
 * @DateTime 2022-09-18 21:46
 * @Version 1.0
 */
@RestController
@Slf4j
@Api("customer接口")
public class CustomerController {
    public static final String CACHE_KEY_USER = "user";

    @Resource
    private CustomerService customerService;

    @Resource
    private RedisTemplate redisTemplate;

    @ApiOperation("新增客户信息")
    @PostMapping("/customer/add")
    public void add(Customer customer){
        Integer i = customerService.add(customer);
        //判断数据是否添加到MySQL数据库成功
        if (i > 0){
            //2.成功后,从mysql数据库中拿到主键ID作为redis的key值存入redis数据库
            Customer customer1 = customerService.getCustomerById(customer.getId());
            String key = customer.getId() + CACHE_KEY_USER;
            redisTemplate.opsForValue().set(key,customer1.toString());
        }
    }

    @ApiOperation("根据ID删除客户信息")
    @DeleteMapping("/customer/delete/{id}")
    public void delete(@PathVariable Integer id){
        Integer i = customerService.delete(id);
        //判断MySQL数据库中的数据是否删除成功
        if (i > 0){
            //2.成功后,根据用户传给主键ID删除redis的数据
            redisTemplate.delete(id+CACHE_KEY_USER);
        }
    }

    @ApiOperation("更新客户信息")
    @PutMapping("/customer/update/{id}")//这里用户直接传id值来确定更新的用户
    public void update(@PathVariable Integer id, Customer customer){
        Integer i = customerService.update(customer);
        //判断MySQL数据库中的数据是否删除成功
        if (i > 0){
            //2.成功后,从mysql数据库中拿到主键ID作为redis的key值存入redis数据库直接覆盖原来数据,达到更新效果
            Customer customer1 = customerService.getCustomerById(customer.getId());
            String key = customer.getId() + CACHE_KEY_USER;
            redisTemplate.opsForValue().set(key,customer1.toString());
        }
    }

    @ApiOperation("根据id查询的顾客信息--小中厂-QPS<1000")
    @GetMapping("/customer/{id}")
    public Customer getCustomerById(@PathVariable Integer id){
        //1.先在redis数据库中查看用户信息是否存在
        Customer customer = (Customer) redisTemplate.opsForValue().get(CACHE_KEY_USER + id);
        if (customer == null) {
            //2.判断MySQL数据库中是否存在用户信息
            Customer customer1 = customerService.getCustomerById(id);
            if (customer1 == null) {
                return null;
            }else {
                String key = id + CACHE_KEY_USER;
                redisTemplate.opsForValue().set(key,customer1.toString());
                return customer1;
            }
        }else {
            return customer;
        }
    }

    @ApiOperation("根据id查询的顾客信息--大厂")
    @GetMapping("/customer/get/{id}")
    public Customer getCustomerById1(@PathVariable Integer id){
        /**
         * 加强补充:避免key突然失效,打爆MySQL,做一下预防,尽量不出现击穿的情况
         *
         * 理由:由于实际工作中,一般redis的key都是会被设置过期时间的,如果突然碰到过期时间到期,某个热点key突然失效,就....了
         *
         * 会发生redis中热点key突然失效导致的缓存击穿问题
         *
         * 缓存击穿:redis之前有,key热点失效了,打在MySQL上
         * 缓存穿透:两个都是null
         */
        //1.先在redis数据库中查看用户信息是否存在
        Customer customer = (Customer) redisTemplate.opsForValue().get(CACHE_KEY_USER + id);
        if (customer == null) {
            //2.大厂用,对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿MySQL
            synchronized (CustomerController.class){
                Customer customer1 = (Customer) redisTemplate.opsForValue().get(CACHE_KEY_USER + id);
                //3. 二次查redis还是null,可以去查MySQL了
                if (customer1 == null){
                    //4.查询MySQL拿数据
                    Customer customer2 = customerService.getCustomerById(id);//mysql默认有数据
                    if(customer2 == null){
                        return null;
                    }else {
                        //5. MySQL里面有数据的,需要回写redis,完成数据的一致性工作
                        redisTemplate.opsForValue().setIfAbsent(CACHE_KEY_USER + id,customer2.toString(),7L, TimeUnit.DAYS);
                        return customer2;
                    }
                }else {
                    return customer1;
                }
            }

        }else {
            return customer;
        }
    }
}
